#pragma once


/*
* class World contains all models of the game
*/
#include "IRenderable.h"
#include "IAnimated.h"
#include "GeometryUtils.h"
#include "Balloon.h"
#include "BalloonsShop.h"
#include "ModelEvents.h"
#include <vector>
#include <memory>

class Tree;
class FerrisWheel;
class Model3DStorage;
class WorldObject;
class Human;
class WorldInfoController;
class Bench;
class HeightMap;

/*
* All items should be created inside World class
*/

class Ground;

class World
  {
  public:
    HSMODEL_API World();
    HSMODEL_API World(const HeightMap &i_heightmap);
    HSMODEL_API ~World();

    HSMODEL_API std::map<std::string, IRenderable*> Renderable() const;
    HSMODEL_API std::vector<IAnimated*> Animated() const;
    HSMODEL_API std::vector<IMovable*> Movable() const;

    HSMODEL_API IRenderable* GetRenderableByName(const std::string& i_name) const;

    template<class C>
    std::vector<C*> GetItems() const;

    template <typename T> void RefreshObjectsOnGroundPositions();
    
    HSMODEL_API const Ground* GetGround() const;
    HSMODEL_API Ground* GetGround();

    HSMODEL_API const std::shared_ptr<WorldInfoController> GetWorldInfoController() const;

    void DeleteObj(const WorldObject* i_object);  

    HSMODEL_API void AddTree(const TVector3& i_position);
    HSMODEL_API void AddFerrisWheel(const TVector3& i_position); 
    HSMODEL_API void AddBench(TVector2 i_point);
    HSMODEL_API void AddHuman(const TVector3& i_position);
    HSMODEL_API void AddHuman(const TVector3& i_position, const EBalloonColor i_color);
    HSMODEL_API void AddBalloonsShop(const TVector3& i_position);

    HSMODEL_API void AddRenderable(IRenderable *i_renderable);
	  HSMODEL_API void AddAnimated(IAnimated *i_animated);
    HSMODEL_API void AddMovable(IMovable *i_movable);

    HSMODEL_API void RemoveRenderable(IRenderable *i_renderable);
    HSMODEL_API void RemoveAnimated(IAnimated *i_animated);
    HSMODEL_API void RemoveMovable(IMovable *i_movable);

    HSMODEL_API void SubscribeOnChangeEvent(Observer i_callback) const;
    HSMODEL_API void GenerateChangeEvent();

    HSMODEL_API void SubscribeOnChangeObjectWithoutLandscapeEvent(Observer i_callback) const;
    HSMODEL_API void GenerateChangeObjectWithoutLandscapeEvent();

    HSMODEL_API void SubscribeOnLandscapeChangeEvent(Observer i_callback) const;
    HSMODEL_API void GenerateLandscapeChangeEvent();

    HSMODEL_API void SubscribeOnMoveEvent(Observer i_callback) const;
    HSMODEL_API void GenerateMovedEvent(const std::string& i_object_name) const;

    HSMODEL_API void SubscribeOnAnimateEvent(Observer i_callback) const;
    HSMODEL_API void GenerateAnimateEvent(const std::string& i_object_name) const;

    HSMODEL_API TVector3 GetObjectPosition(const std::string& i_object_name) const;
    HSMODEL_API double GetObjectAngleAroundY(const std::string& i_object_name) const;
		HSMODEL_API void ClearModels();
		HSMODEL_API void ResetGround(std::unique_ptr<Ground>& i_ground);
  private:
    std::vector<std::shared_ptr<WorldObject>> m_world_objects;
    std::map<std::string, IRenderable*> m_renderable;
	  std::vector<IAnimated*> m_animated;
    std::vector<IMovable*> m_movable;

    std::unique_ptr<Ground> mp_ground;

    std::shared_ptr<WorldInfoController> mp_world_info_controller;
    mutable SimpleEvent m_changed_event;
    mutable SimpleEvent m_objects_without_landscape_changed_event;
    mutable SimpleEvent m_landscape_changed_event;
    mutable SimpleEvent m_object_moved_event;
    mutable SimpleEvent m_animate_event;
  };

template<class C>
std::vector<C*> World::GetItems() const
  {
  std::vector<C*> result;
  for (auto p_renderable : m_world_objects)
    {
    if (auto obj = dynamic_cast<C*>(p_renderable.get()))
      result.push_back(obj);
    }
  return result;
  }

template HSMODEL_API void World::RefreshObjectsOnGroundPositions<Tree>();
template HSMODEL_API void World::RefreshObjectsOnGroundPositions<FerrisWheel>();
template HSMODEL_API void World::RefreshObjectsOnGroundPositions<BalloonsShop>();
template HSMODEL_API void World::RefreshObjectsOnGroundPositions<Bench>();
